/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "IMovingClipMapTerrain.h"
#include "core/core.h"
#include "geom/mathutils.h"

#ifdef CORE_DIRECTX
#include "dx/MovingClipMapTerrain.h"
#endif

namespace terrain {

IMovingClipMapTerrain::IMovingClipMapTerrain(void)
:	m_uiTesselation(0),
	m_uiNumVertices(0),
	m_pVertices(NULL),
	m_pTexCoords(NULL),
	m_pDetailTexCoords(NULL),
	m_pIndices(NULL),
	m_pTerrainShader(NULL),
	m_pCamera(NULL),
	vInvTess(1,1),
	vTexSize(0,0),
	fOffsetScale(0.40f),
	m_bUseR2VB(false),
	pRenderer(NULL)
{
	pRenderer = coreGetRenderer();
	m_pCamera = pRenderer->getCamera();
}

IMovingClipMapTerrain::~IMovingClipMapTerrain(void)
{
	DELETE_ARRAY(m_pVertices);
	DELETE_ARRAY(m_pTexCoords);
	DELETE_ARRAY(m_pDetailTexCoords);
	DELETE_ARRAY(m_pIndices);
	m_pCamera = NULL;
	pRenderer = NULL;
}

IMovingClipMapTerrain* IMovingClipMapTerrain::Create()
{
	IMovingClipMapTerrain* resource = NULL;
#ifdef CORE_DIRECTX
	resource = new directx::MovingClipMapTerrain();
#endif
	return resource;
}

void IMovingClipMapTerrain::init(int n) 
{
	// Set the tesselation
	m_uiTesselation = static_cast<int>( pow(2.0, n) ) + 1;
	// Set the half tesselation to center the map
	int halfTess = m_uiTesselation / 2;
	// Set the inverse of the tesselation as standard
	float tesInverse = 1.0f/static_cast<float>(m_uiTesselation);
	vInvTess = math::Vector2f(tesInverse, tesInverse);
	//vInvTess.print();

	// Load the shader
	m_pTerrainShader = coreGetResourceFactory()->loadShaderEffect( coreGetWorkingDirectory() + "\\terrain\\terrain.fx");
	if (m_pTerrainShader) {
		hTextureSize  = m_pTerrainShader->getParameterByName("textureSize");
		hOffset       = m_pTerrainShader->getParameterByName("offset");
		hDetailOffset = m_pTerrainShader->getParameterByName("detailoffset");
		hInvOffsetScale = m_pTerrainShader->getParameterByName("invOffsetScale");
		hInvTess        = m_pTerrainShader->getParameterByName("invTess");
		hScaleFactor    = m_pTerrainShader->getParameterByName("scaleFactor");

		hBlendMap       = m_pTerrainShader->getParameterByName("blendmap");
		hDetailMap1     = m_pTerrainShader->getParameterByName("detailmap1");
		hDetailMap2     = m_pTerrainShader->getParameterByName("detailmap2");
		hDetailMap3     = m_pTerrainShader->getParameterByName("detailmap3");
		hHeightMap      = m_pTerrainShader->getParameterByName("heightmap");
		hNormalMap      = m_pTerrainShader->getParameterByName("normalmap");
		hGrassMap       = m_pTerrainShader->getParameterByName("grassmap");
	}

	// Before doing anything, check for capabilities:
	if (coreGetRenderer()->supportsR2VB())
		m_bUseR2VB = true;

	// Set the number of vertices
	m_uiNumVertices = m_uiTesselation * m_uiTesselation;
	// Allocate memory for the vertices
	m_pVertices = new math::Vector3[m_uiNumVertices];
	m_pTexCoords = new math::Vector2f[m_uiNumVertices];
	m_pDetailTexCoords = new math::Vector2f[m_uiNumVertices];

	m_pIndices = (WORD*) malloc(sizeof(WORD) * m_uiNumVertices * 6 );

	for (unsigned int z=0; z<m_uiTesselation; ++z) {
		for (unsigned int x=0; x<m_uiTesselation; ++x) {
			m_pVertices[z*m_uiTesselation + x] = math::Vector3( (static_cast<float>(x)-halfTess), 0, (static_cast<float>(z)-halfTess) );
			m_pTexCoords[z*m_uiTesselation + x] = math::Vector2f( x*tesInverse+.5f, 1.0f-z*tesInverse-.5f );
			m_pDetailTexCoords[z*m_uiTesselation + x] = math::Vector2f( static_cast<float>(x*fOffsetScale), static_cast<float>(z*fOffsetScale) );
		}
	}

	for (unsigned int z=0; z<(m_uiTesselation-1); ++z) {
		for (unsigned int x=0; x<(m_uiTesselation-1); ++x) {
			int arrindex  = (z * (m_uiTesselation-1) + x) *6;
			int vertindex = (z * m_uiTesselation + x);

			m_pIndices[arrindex  ] = vertindex;
			m_pIndices[arrindex+1] = vertindex + m_uiTesselation;
			m_pIndices[arrindex+2] = vertindex + 1;
			m_pIndices[arrindex+3] = vertindex + m_uiTesselation;
			m_pIndices[arrindex+4] = vertindex + m_uiTesselation + 1;
			m_pIndices[arrindex+5] = vertindex + 1;
		}
	}
}

const int IMovingClipMapTerrain::getTesselation() const
{
	return m_uiTesselation;
}

void IMovingClipMapTerrain::setHeightmap(ITexture *pTexture)
{
	ITerrain::setHeightmap(pTexture);

	// Get the texture size and calculate the inverse
	float invW = 1.0f / static_cast<float>(pTexture->getWidth());
	float invH = 1.0f / static_cast<float>(pTexture->getHeight());
	vInvTess = math::Vector2f(invW, invH);
	//vInvTess.print();
	// Since the terrain is centered around 0, we need to subtract half the tesselation
	float halftestW = (m_uiTesselation * invW) / 2.0f;
	float halftestH = (m_uiTesselation * invH) / 2.0f;
	// Reset the texcoords
	for (unsigned int y=0; y<m_uiTesselation; ++y) {
		for (unsigned int x=0; x<m_uiTesselation; ++x) {
			m_pTexCoords[y*m_uiTesselation + x] = math::Vector2f( (x*invW - halftestW+invW), 
																  1.0f - (y*invH - halftestH) );
		}
	}

	vTexSize = math::Vector2f( static_cast<float>(m_pHeightmap->getWidth()), static_cast<float>(m_pHeightmap->getHeight()) );
}


/// Calculates the bounding sphere for visibility testing
void IMovingClipMapTerrain::calculateBoundingSphere()
{
	m_bbSphere.setRadius(10000);
}

} // namespace
